package com.chenju.javastatic.service.impl;

import com.chenju.javastatic.adapter.*;
import com.chenju.javastatic.common.enums.EasyCode;
import com.chenju.javastatic.component.config.TargetProjectConfig;
import com.chenju.javastatic.domain.entity.*;
import com.chenju.javastatic.mybatis.mapper.*;
import com.chenju.javastatic.service.EasyTestService;
import com.chenju.javastatic.utils.EasyListUtils;
import com.chenju.javastatic.utils.EasyJsonUtils;
import com.chenju.javastatic.utils.EasyVisitUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.JarFile;

@Service
public class EasyTestServiceImplements implements EasyTestService {
    private final static Logger logger = LoggerFactory.getLogger(EasyTestServiceImplements.class);

    // 创建线程池
    ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    List<Future<?>> futures = new ArrayList<>();

    private final ClassMapper classMapper;
    private final MethodMapper methodMapper;
    private final MethodInsnMapper methodInsnMapper;
    private final AnnotationMapper annotationMapper;
    private final InterfaceMapper interfaceMapper;
    private final FeignCallMapper feignCallMapper;

    private final List<String> jarPathList = TargetProjectConfig.getJarPathArray();
    private final List<String> applicationList = TargetProjectConfig.getApplicationArray();

    public EasyTestServiceImplements(ClassMapper classMapper, MethodMapper methodMapper, MethodInsnMapper methodInsnMapper, AnnotationMapper annotationMapper, InterfaceMapper interfaceMapper, FeignCallMapper feignCallMapper) {
        this.classMapper = classMapper;
        this.methodMapper = methodMapper;
        this.methodInsnMapper = methodInsnMapper;
        this.annotationMapper = annotationMapper;
        this.interfaceMapper = interfaceMapper;
        this.feignCallMapper = feignCallMapper;
    }

    @Override
    public void parseProject() {
        if (jarPathList.size() != applicationList.size()){
            logger.info(String.format("配置文件【targetproject.properties】中jar文件数量:%s 与application数量:%s 不一致!", jarPathList.size(), applicationList.size()));
            return;
        }

        // 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 初始化数据库
        initDB();

        for (int i = 0 ; i < jarPathList.size(); i++) {
            String jarFilePath = jarPathList.get(i);
            String applicationName = applicationList.get(i);
            parse(jarFilePath, applicationName);
        }

        // 更新调用关系
        updateMethodInsn();

        stopWatch.stop();
        logger.info(String.format("parse Project执行耗时: %sms", stopWatch.getTime()));
    }

    @Override
    public void parse(String jarFilePath, String applicationName) {
        // 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 初始化数据库
//         initDB();

        try (JarFile jarFile = new JarFile(new File(jarFilePath))) {
            jarFile.stream().forEach(jarEntry -> {
                if (jarEntry.getName().endsWith(".class") && EasyVisitUtils.isVisitClass(jarEntry.getName())) {
                    try {
                        // 创建 ClassReader
                        ClassReader classReader = new ClassReader(jarFile.getInputStream(jarEntry));
                        // 创建 ClassVisitor，并实现你需要的逻辑
                        ClassVisitorAdapter cna = new ClassVisitorAdapter(jarFile);
                        //classReader.accept(cna, 0);
                        classReader.accept(cna, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                        // 处理逻辑
                        insertClass(cna, applicationName);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 更新调用关系中的方法id
//         updateMethodInsn();

        stopWatch.stop();
        logger.info(String.format("parse Jar File: %s 方法执行耗时: %sms",jarFilePath, stopWatch.getTime()));
    }

    public void initDB(){
        methodInsnMapper.deleteAll();
        annotationMapper.deleteAll();
        methodMapper.deleteAll();
        classMapper.deleteAll();
        interfaceMapper.deleteAll();
    }

    public void updateMethodInsn() {
        // 定义list批量更新
        List<MethodInsnEntity> updateMethodInsns = new ArrayList<>();

        List<MethodInsnEntity> methodInsnEntities = methodInsnMapper.selectAll();
        if (!methodInsnEntities.isEmpty()){
            for (MethodInsnEntity methodInsnEntity : methodInsnEntities) {
                String name = methodInsnEntity.getName();
                String descriptor = methodInsnEntity.getDescriptor();
                String owner = methodInsnEntity.getClassName();
                System.out.println("结果是" + name +":"+ descriptor +":"+ owner);
                MethodEntity method = methodMapper.selectByNameAndDescAndClassName(name, descriptor, owner);
                if (method == null) {
                    // TODO: 还是要处理extends
                    continue;
                }
                updateMethodInsns.add(new MethodInsnEntity(methodInsnEntity.getId(), method.getId()));
            }
        }
        if (!updateMethodInsns.isEmpty()){
            methodInsnMapper.batchUpdate(updateMethodInsns);
        }
    }

    public void insertClass(ClassVisitorAdapter cna, String applicationName) {
        String className = cna.name;
        String superName = cna.superName;
        OuterMethodVisitorAdapter outerMethod = cna.outerMethod;
        int isInterface = cna.isInterface;
        int isController = cna.isController ? 1 : 0;
        int isFeignClient = cna.isFeignClient ? 1 : 0;
        int isAspect = cna.isAspect ? 1 : 0;
        int isConfiguration = cna.isConfiguration ? 1 : 0;
        String mappingPaths = cna.isController ? EasyJsonUtils.listToJson(cna.mappingPaths) : "";
        String interfacesStr = EasyListUtils.asString(cna.interfaces, ",");
        List<AnnotationVisitorAdapter> annotations = cna.visibleAnnotations;
        List<MethodVisitorAdapter> methods = cna.methods;

        // 将class插入数据库
        ClassEntity classEntity = new ClassEntity(className, superName, interfacesStr, applicationName, isInterface, isController, mappingPaths, isFeignClient, isAspect, isConfiguration);
        classMapper.insert(classEntity);
        Long id = classEntity.getId();

        // 创建Annotations
        if (!annotations.isEmpty()) {
            insertAnnotation(annotations, id, EasyCode.CLASS_TYPE);
        }

        // 创建methods
        if (!methods.isEmpty()) {
            insertMethods(methods, classEntity, outerMethod);
        }
    }

    public void insertMethods(List<MethodVisitorAdapter> methods, ClassEntity classEntity, OuterMethodVisitorAdapter outerClassMethod) {
        for (MethodVisitorAdapter method : methods) {
            int isMapping = method.isMapping ? 1 : 0;
            String name = method.name;
            String desc = method.desc;
            String owner = method.owner;
            List<String> requestMethods = method.isMapping ? method.requestMethods : new ArrayList<>();
            List<String> requestPaths = method.isMapping ? method.requestPaths : new ArrayList<>();
            List<String> classRequestMappingPath = EasyJsonUtils.jsonToList(classEntity.getMappingPaths());
            Long foreignId = classEntity.getId();
            List<AnnotationVisitorAdapter> annotations = method.visibleAnnotations;
            List<MethodInsnVisitorAdapter> methodInsns = method.methodInsns;
            OuterMethodVisitorAdapter outerMethod = method.outerMethod;
            MethodEntity methodEntity = new MethodEntity(name, desc, owner, foreignId, isMapping);
            methodMapper.insert(methodEntity);
            Long methodId = methodEntity.getId();

            // 插入annotation
            if(!annotations.isEmpty()){
                insertAnnotation(annotations, methodId, EasyCode.METHOD_TYPE);
            }

            // TODO: 插入parameter

            // 插入methodInsns
            if (!methodInsns.isEmpty()) {
                insertMethodInsns(methodInsns, methodId);
            }

            // 插入extends或implements
            if (outerMethod != null) {
                insertMethodOuters(outerMethod, methodId);
            }

            // 插入OuterClassMethod
            if (outerClassMethod != null){
                insertOuterClassMethod(outerClassMethod, methodId);
            }

            // 插入接口信息
            Long interfaceId = null;
            if (isMapping == 1 && classEntity.getIsController() == 1){
                insertInterface(requestMethods, classRequestMappingPath, requestPaths, methodEntity, classEntity.getApplication());
            }

            // 插入Feign调用信息
            if (isMapping == 1 && classEntity.getIsFeignClient() == 1) {
                // 获取FeignClient中value的值
                String annotationValues = annotationMapper.selectValues(EasyCode.ANNOTATION_FEIGNCLIENT, classEntity.getId(), EasyCode.CLASS_TYPE);
                Map<String, Object> annotationMap = EasyJsonUtils.jsonToMap(annotationValues);
                if (annotationMap != null){
                    String targetApplicationName = annotationMap.get("value").toString();
                    insertFeignCall(targetApplicationName, methodEntity.getId(), requestPaths);
                }
            }
        }
    }

    // 插入Feign调用信息
    public void insertFeignCall(String applicationName, Long methodId, List<String> requestPaths){
        feignCallMapper.insert(new FeignCallEntity(methodId, EasyJsonUtils.listToJson(requestPaths), applicationName));
    }

    // 插入接口信息
    // TODO: 这里考虑对于有多个方法和多个路径时组合创建多个Interface，因为不确定在Feign调用中是会指定方法还是怎么操作的，后面碰到问题再修复
    public void insertInterface(List<String> requestMethods, List<String> classMappingPaths, List<String> requestPaths, MethodEntity methodEntity, String application) {
        Long foreignId = methodEntity.getId();
        String name = methodEntity.getName();
        String requestMethodsStr = EasyJsonUtils.listToJson(requestMethods);
        String requestPathsStr = joinMappingPath(classMappingPaths, requestPaths);
        interfaceMapper.insert(new InterfaceEntity(name, requestMethodsStr, requestPathsStr, foreignId, application));
    }

    // 处理outer class中的method，direction=0
    public void insertOuterClassMethod(OuterMethodVisitorAdapter outerClassMethod, Long methodId) {
        String owner = outerClassMethod.owner;
        String name = outerClassMethod.name;
        String descriptor = outerClassMethod.descriptor;
        String outerType = outerClassMethod.outerType;

        methodInsnMapper.insert(new MethodInsnEntity(name, descriptor, owner, null, outerType, null, methodId, 0));
    }

    // 处理implements或extends
    public void insertMethodOuters(OuterMethodVisitorAdapter outerMethod, Long foreignId) {
        // 构建List批量插入
        List<MethodInsnEntity> insertMethodOuters = new ArrayList<>();

        String type = outerMethod.outerType;
        String name = outerMethod.name;
        String descriptor = outerMethod.descriptor;
        String owner = outerMethod.owner;
        // 当为实现类时, direction=0
        if (EasyCode.INVOKEIMPLEMENTS_TYPE.equals(type)) {
            MethodInsnEntity entity = new MethodInsnEntity(name, descriptor, owner, null, type, null, foreignId, 0);
            insertMethodOuters.add(entity);
        }
        // 当为超类时, direction=1
        if (EasyCode.INVOKEEXTENDS_TYPE.equals(type)) {
            MethodInsnEntity entity = new MethodInsnEntity(name, descriptor, owner, null, type, null, foreignId, 1);
            insertMethodOuters.add(entity);
        }

        if (!insertMethodOuters.isEmpty()) {
            methodInsnMapper.batchInsert(insertMethodOuters);
        }
    }

    public void insertMethodInsns(List<MethodInsnVisitorAdapter> methodInsns, Long foreignId) {
        // 构建要批量插入的methodInsns
        List<MethodInsnEntity> insertMethodInsns = new ArrayList<>();
        for (MethodInsnVisitorAdapter methodInsn : methodInsns) {
            String name = methodInsn.name;
            String desc = methodInsn.descriptor;
            String owner = methodInsn.owner;

            MethodInsnEntity entity = new MethodInsnEntity(name, desc, owner, null, methodInsn.invokeType, null, foreignId, 1);
            if (!insertMethodInsns.contains(entity)){
                insertMethodInsns.add(entity);
            }
        }
        methodInsnMapper.batchInsert(insertMethodInsns);
    }

    public void insertAnnotation(List<AnnotationVisitorAdapter> annotations, Long foreignId, String annotationType){
        // 构建要批量插入的annotations
        List<AnnotationEntity> insertAnnotations = new ArrayList<>();
        for (AnnotationVisitorAdapter annotation : annotations){
            String name = Type.getType(annotation.desc).getClassName();
            String valuesString = annotation.valuesMap == null ? "" : EasyJsonUtils.mapToJson(annotation.valuesMap);
            // 构建实体类
            AnnotationEntity entity = new AnnotationEntity(name, valuesString, foreignId, annotationType);
            if (!insertAnnotations.contains(entity)){
                insertAnnotations.add(entity);
            }
        }
        // 存储实体类
        if (!insertAnnotations.isEmpty()){
            annotationMapper.batchInsert(insertAnnotations);
        }
    }

    public String joinMappingPath(List<String> classMappingPaths, List<String> requestMappingPaths){
        List<String> fullMappingPaths = new ArrayList<>();
        for (String classMappingPath : classMappingPaths) {
            for (String requestPath : requestMappingPaths) {
                fullMappingPaths.add(classMappingPath + requestPath);
            }
        }
        return EasyJsonUtils.listToJson(fullMappingPaths);
    }

    @Deprecated
    public void insertImplements(){
        // 查找classInfo中所有IMPLEMENTS
        List<ClassEntity> classEntities = classMapper.selectInterfacesNotNull();

        // 构建List批量插入
        List<MethodInsnEntity> insertImplementsMethodInsns = new ArrayList<>();

        if (!classEntities.isEmpty()){
            for (ClassEntity classEntity : classEntities) {
                String className = classEntity.getName();
                for (String interfaceName : classEntity.getInterfaces().split(",")) {
                    // 查询该interface下的方法
                    Long interfaceClassId = classMapper.selectIdByName(interfaceName);
                    List<MethodEntity> interfaceMethods = methodMapper.selectByClassId(interfaceClassId);
                    for (MethodEntity interfaceMethod : interfaceMethods){
                        // 构建新的方法调用
                        String methodName = interfaceMethod.getName();
                        String descriptor = interfaceMethod.getDescriptor();
                        // 被调用的方法名
                        String invokeMethodName = getSimpleName(methodName);
                        insertImplementsMethodInsns.add(new MethodInsnEntity(invokeMethodName, descriptor, className, null, EasyCode.INVOKEIMPLEMENTS_TYPE, null, interfaceMethod.getId(), 1));
                    }
                }
            }
        }
        if (!insertImplementsMethodInsns.isEmpty()) {
            methodInsnMapper.batchInsert(insertImplementsMethodInsns);
        }
    }

    public String getSimpleName(String fullName) {
        return fullName.substring(fullName.lastIndexOf(".") + 1);
    }
}
